#include "RingBuffer.h"
#include <string.h>
//#include <util/atomic.h>

int atomicaset(int& base, int value) {
    //ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
        base = value;
    //}
    return base;
}

RingBuffer::HeadInfo::HeadInfo()
    : m_size(0)
    , m_read(0)
    , m_write(0) {
}

RingBuffer::RingBuffer()
    : m_data(nullptr)
    , m_head() {
}

RingBuffer::~RingBuffer() {
    if (m_data != nullptr) {
        delete[] m_data;
        m_data = nullptr;
    }
}

void RingBuffer::init(int size) {
    if (m_data != nullptr) {
        delete[] m_data;
        m_data = nullptr;
    }
    m_head.m_size = size;
    if (size > 0) {
        m_data = new char[m_head.m_size];
    }
    clear();
}

void RingBuffer::clear() {
    atomicaset(m_head.m_read, 0);
    atomicaset(m_head.m_write, 0);
}

void RingBuffer::copy(const RingBuffer& buff) {
    auto size = buff.m_head.m_size;
    init(size);
    if (size > 0) {
        memcpy(m_data, buff.m_data, size);
    }
}

bool RingBuffer::write(const char* src, int size) {
    if (src == nullptr || size <= 0 || space() < size) {
        return false;
    }
    if (m_head.m_write >= m_head.m_read) {
        int hasSpace = m_head.m_size - m_head.m_write;
        if (hasSpace >= size) {
            memcpy(m_data + m_head.m_write, src, size);
        } else {
            memcpy(m_data + m_head.m_write, src, hasSpace);
            memcpy(m_data, src + hasSpace, size - hasSpace);
        }
    } else {
        memcpy(m_data + m_head.m_write, src, size);
    }
    atomicaset(m_head.m_write, (m_head.m_write + size) % m_head.m_size);
    return true;
}

bool RingBuffer::read(char* ptr, int& size) {
    if (peek(ptr, size) == false || size <= 0) {
        return false;
    }
    peekSize(size);
    return true;
}

void RingBuffer::peekSize(int size) {
    atomicaset(m_head.m_read, (m_head.m_read + size) % m_head.m_size);
}

bool RingBuffer::peek(char* ptr, int& size) {
    if (ptr == nullptr || size <= 0) {
        return false;
    }
    auto len = length();
    if (size > len) {
        size = len;
    }
    if (m_head.m_write >= m_head.m_read) {
        memcpy(ptr, m_data + m_head.m_read, size);
    } else {
        int hasSpace = m_head.m_size - m_head.m_read;
        if (hasSpace >= size) {
            memcpy(ptr, m_data + m_head.m_read, size);
        } else {
            memcpy(ptr, m_data + m_head.m_read, hasSpace);
            memcpy(ptr + hasSpace, m_data, size - hasSpace);
        }
    }
    return true;
}